package com.heima.admin.service.impl;
import java.util.Date;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.admin.mapper.AdUserMapper;
import com.heima.admin.mapper.ApUserMapper;
import com.heima.admin.mapper.ApUserRealnameMapper;
import com.heima.admin.service.AdUserService;
import com.heima.common.exception.CustException;
import com.heima.feigns.ArticleFeign;
import com.heima.feigns.WemediaFeign;
import com.heima.file.service.FileStorageService;
import com.heima.model.admin.dtos.AdUserDTO;
import com.heima.model.admin.pojo.AdUser;
import com.heima.model.admin.vos.AdUserVO;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.common.constants.admin.AdminConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.threadlocal.WmThreadLocalUtils;
import com.heima.model.user.dtos.AuthDTO;
import com.heima.model.user.pojos.ApUser;
import com.heima.model.user.pojos.ApUserRealname;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.utils.common.AppJwtUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;

@Slf4j
@Service
public class AdUserServiceImpl extends ServiceImpl<AdUserMapper,AdUser> implements AdUserService {
    @Transactional
    @Override
    public ResponseResult login(AdUserDTO dto) {
        // 1. 校验参数   保证 name  password 不为空
        String name = dto.getName();
        String password = dto.getPassword();
        if(StringUtils.isBlank(name)||StringUtils.isBlank(password)){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"用户名或密码不能为空");
        }
        // 2. 根据name 查询用户信息
        AdUser adUser = this.getOne(Wrappers.<AdUser>lambdaQuery().eq(AdUser::getName, name));
        if (adUser == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"用户不存在");
        }
        // 3. 判断输入的密码是否和数据库中密码一致
        String inputPwd = DigestUtils.md5DigestAsHex((password + adUser.getSalt()).getBytes());
        if(!inputPwd.equals(adUser.getPassword())){
            CustException.cust(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR,"用户密码错误");
        }
        // 4. 判断用户状态是否正确  9(正常)
        if (adUser.getStatus().intValue() != 9) {
            CustException.cust(AppHttpCodeEnum.LOGIN_STATUS_ERROR);
        }
        // 5. 修改最近登陆时间
        adUser.setLoginTime(new Date());
        this.updateById(adUser);
        // 6. 颁发token
        String token = AppJwtUtil.getToken(Long.valueOf(adUser.getId()));

        // 7. 封装返回结果 token  user
        Map result = new HashMap<>();
        result.put("token",token);
        // 通过BeanUtils拷贝 同名属性
        AdUserVO adUserVO = new AdUserVO();
        BeanUtils.copyProperties(adUser,adUserVO);
        result.put("user",adUserVO);
        return ResponseResult.okResult(result);
    }


    /**
     * app端发起实名认证
     */
    @Autowired
    ApUserMapper apUserMapper;
    @Autowired
    FileStorageService fileStorageService;
    @Value("${file.oss.prefix}")
    String prefix;
    @Value("${file.oss.web-site}")
    String webSite;
    @GlobalTransactional(rollbackFor = Exception.class,timeoutMills = 300000)
    @Override
    public ResponseResult AIupdateStatusById(AuthDTO dto, Map <Integer,MultipartFile> map) {
        ApUserRealname apUserRealname = new ApUserRealname();

        map.forEach((integer, multipartFile) -> {
            //1.检查实名认证信息
            //1.1 常规非空判断
            if (multipartFile == null || multipartFile.getSize() <=0) {
                CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"请上传正确的文件");
            }
            WmUser user = WmThreadLocalUtils.getUser();
            if (user == null) {
                CustException.cust(AppHttpCodeEnum.NEED_LOGIN);
            }
            if(dto.getId() == null){
                CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"实名认证id 不能为空");
            }
            //1.2 判断用户id是否重复
            if (createUserId(dto.getId())){
                CustException.cust(AppHttpCodeEnum.DATA_EXIST,"已上传实名认证，请勿重复上传");
            }
            //2.判断图片信息并上传
            String filePath = uploading(multipartFile);

            //2.1保存图片路径
            switch (integer){
                case 0 :
                    apUserRealname.setFontImage(webSite +filePath);
                    //2.2通过OCR接口读取照片中的信息
                    filePath += webSite;
                    OCRCread(apUserRealname,filePath);
                    break;
                case 1 :
                    apUserRealname.setBackImage(webSite +filePath);
                    break;
                case 9 :
                    apUserRealname.setLiveImage(webSite +filePath);
                    break;
                default:
                    CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"参数错误");
            }

        });

        //3.保存信息
        apUserRealname.setUserId(dto.getId());
        apUserRealname.setStatus((short)1);
        apUserRealname.setCreatedTime(new Date());
        apUserRealname.setSubmitedTime(new Date());
        apUserRealname.setUpdatedTime(new Date());

        apUserRealnameMapper.insert(apUserRealname);

        //4.根据AI自动实名认证
        updateStatusById(dto);

        return ResponseResult.okResult();
    }

    @Override
    public ResponseResult updateStatusById(AuthDTO dto) {
        // 1. 校验参数   (实名认证id 不能为空   )
        if(dto.getId() == null){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"实名认证id 不能为空");
        }
        // 2. 根据实名认证的id  查询 ap_user_realname数据
        // ap_user_realname (userId)
        ApUserRealname userRealname = apUserRealnameMapper.selectById(dto.getId());
        // 3. 判断实名认证的状态 是否 为 待审核  ( 1, 2, 9)
        if(!AdminConstants.WAIT_AUTH.equals(userRealname.getStatus())){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW,"实名认证的状态不是待审核状态");
        }
        // 3.1 调用AI检测
        Short status = AICertification(dto);
        // 4. 根据实名认证信息关联的apUserId 查询出 apUser信息               ap_user
        ApUser apUser = apUserMapper.selectById(userRealname.getUserId());
        if(apUser == null){
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"实名认证关联的用户信息不存在");
        }
        // 5. 修改实名认证的状态
        userRealname.setStatus(status);
        if(StringUtils.isNotBlank(dto.getMsg())){
            // 驳回原因
            userRealname.setReason(dto.getMsg());
        }
        // 修改状态完毕
        apUserRealnameMapper.updateById(userRealname);
        // 6. 判断 状态是2 (审核失败 方法结束)  还是 9
        if(AdminConstants.FAIL_AUTH.equals(status)){
            return ResponseResult.okResult();
        }
        // 7. 如果是9 代表审核通过:
        // 8. 开通自媒体账户  (查询是否已开通过   保存自媒体账户信息)        wm_user
        WmUser wmUser = createWmUser(apUser);
        // 9.  创建作者信息 (查询是否已经创建    保存作者信息)      ap_author (userId   wmUserId)
        createApAuthor(apUser,wmUser);
        if(dto.getId().intValue() == 5){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"演示异常，用来演示分布式事务问题");
        }
        return ResponseResult.okResult();
    }
    private Short AICertification(AuthDTO dto){

        //1.基于用友云 身份证+姓名 认证是否真实
        RestTemplate restTemplate = new RestTemplate();
        // 请求头的信息
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.setContentType(MediaType.APPLICATION_JSON);
        httpHeaders.set("apicode","baf99b5d34774adf8bb3f185bfc9785e");

        // 请求参数
        ApUserRealname apUserRealname = apUserRealnameMapper
                .selectOne(Wrappers.<ApUserRealname>lambdaQuery()
                        .eq(ApUserRealname::getUserId, dto.getId()));
        Map<String,String> paramMap = new HashMap<>();
        paramMap.put("idNumber",apUserRealname.getIdno());
        paramMap.put("userName",apUserRealname.getName());

        HttpEntity<String> httpEntity = new HttpEntity<>(JSON.toJSONString(paramMap),httpHeaders);

        log.info("身份核验开始");
        ResponseEntity<String> responseEntity = restTemplate.postForEntity("https://api.yonyoucloud.com/apis/dst/matchIdentity/matchIdentity", httpEntity, String.class);
        JSONObject objectAuthentication= JSONObject.parseObject(responseEntity.getBody());
        //"message":"一致"
        String message = objectAuthentication.getJSONObject("data").get("message").toString();
        if (!message.equals("一致")){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"实名验证失败，姓名与身份证号不一致");
            log.info("实名验证失败，姓名与身份证号不一致");
            return 2;
        }
        log.info("身份核验通过");

        //2.基于用友云解析身份背面信息，判断有效期
        log.info("有效期核验开始");
        RestTemplate restTemplateExpiry = new RestTemplate();
        HttpHeaders httpHeadersExpiry = new HttpHeaders();
        httpHeadersExpiry.add("apicode","3cc49b13c720444096329c5398621f79");
        httpHeadersExpiry.setContentType(MediaType.APPLICATION_JSON); // json
        Map<String,String> mapExpiry = new HashMap<>();
        mapExpiry.put("image",apUserRealname.getBackImage());
        mapExpiry.put("imageType","URL");
        mapExpiry.put("ocrType","1");
        HttpEntity<String> formEntry = new HttpEntity<>(JSON.toJSONString(mapExpiry), httpHeadersExpiry); // 封装请求参数
        ResponseEntity<String> response = restTemplate.postForEntity("https://api.yonyoucloud.com/apis/dst/IdcardOCR/IdcardOCR", formEntry, String.class);// 发送一个post请求
        String responseJson = response.toString();
        int i = responseJson.indexOf(",")+1;
        int j = responseJson.indexOf("[")-1;
        responseJson = responseJson.substring(i,j);

        JSONObject objectExpiry= JSONObject.parseObject(responseJson);
        //"message":"成功"
        String messageExpiry = objectExpiry.get("message").toString();
        if (messageExpiry.equals("成功")){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"身份证已过期");
            log.info("身份证已过期");
            return 2;
        }
        log.info("身份证效期验证通过");


        //3.活体检测核验
        log.info("活体检测开始");
        RestTemplate restTemplateLive = new RestTemplate();
        HttpHeaders httpHeadersLive = new HttpHeaders();
        httpHeaders.add("apicode","d4cbe715e4f34605bfc7d7063ecf92e9");
        httpHeaders.setContentType(MediaType.APPLICATION_JSON); // json
        Map<String,String> mapLive = new HashMap<>();
        mapLive.put("idCardImage","https://hmtt122.oss-cn-shanghai.aliyuncs.com/demo_idcard.png");
        mapLive.put("imageType","URL");
        mapLive.put("liveImage","https://haru1987-hmtt.oss-cn-shanghai.aliyuncs.com/Snipaste_2022-05-13_19-44-54.png");
        HttpEntity<String> formEntryLive = new HttpEntity<>(JSON.toJSONString(mapLive), httpHeaders); // 封装请求参数
        ResponseEntity<String> responseLive = restTemplate
                .postForEntity("https://api.yonyoucloud.com/apis/dst/ComparisonsofPersonsandDocuments/ComparisonsofPersonsandDocuments", formEntry, String.class);// 发送一个post请求
        String responseJsonLive = responseLive.toString();

        int iLive = responseJson.indexOf(",")+1;
        int jLive = responseJson.indexOf("[")-1;
        responseJsonLive = responseJsonLive.substring(iLive,jLive);

        JSONObject objectLive= JSONObject.parseObject(responseJsonLive);
        Double liveScore = Double.valueOf((Double) objectLive.getJSONObject("data").get("score"));

        if (liveScore < 80.0){
            log.info("活体检测未通过");
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"活体检测未通过");
            return 2;
        }
        return 9;
    }


    private void OCRCread(ApUserRealname apUserRealname,String filePath) {
        RestTemplate restTemplate = new RestTemplate();
        HttpHeaders httpHeaders = new HttpHeaders();
        httpHeaders.add("apicode","3cc49b13c720444096329c5398621f79");
        httpHeaders.setContentType(MediaType.APPLICATION_JSON); // json
        Map<String,String> map = new HashMap<>();
        map.put("image",filePath);
        map.put("imageType","URL");
        map.put("ocrType","0");
        HttpEntity<String> formEntry = new HttpEntity<>(JSON.toJSONString(map), httpHeaders); // 封装请求参数
        ResponseEntity<String> response = restTemplate.postForEntity("https://api.yonyoucloud.com/apis/dst/IdcardOCR/IdcardOCR", formEntry, String.class);// 发送一个post请求
        String responseJson = response.toString();
        int i = responseJson.indexOf(",")+1;
        int j = responseJson.indexOf("[")-1;
        responseJson = responseJson.substring(i,j);

        JSONObject object= JSONObject.parseObject(responseJson);

        apUserRealname.setName(object.getJSONObject("data").get("name").toString());
        apUserRealname.setIdno(object.getJSONObject("data").get("cardNum").toString());

    }

    private String uploading(MultipartFile multipartFile) {
        //1.获取图片信息
        // 获得原始的文件名称    [jpg  jpeg   png   gif]
        String originalFilename = multipartFile.getOriginalFilename();
        if(!checkFileSuffix(originalFilename)){
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"文件类型不支持， 目前支持:[jpg  jpeg   png   gif] ");
        }
        //2.添加实名认证信息
        //2.1 将图片上传到OSS中

        String filePath = null;
        try {
            String newFileName = UUID.randomUUID().toString().replaceAll("-","");
            // mein.v004.jpg
            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
            filePath = fileStorageService.store(prefix, newFileName + suffix, multipartFile.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
            CustException.cust(AppHttpCodeEnum.SERVER_ERROR,"上传文件到阿里云失败");
        }
        return filePath;
    }


    @Autowired
    ApUserRealnameMapper apUserRealnameMapper;
    private boolean createUserId(Integer id) {
        //1. 根据用户id查询是否已存在上传的实名信息
        ApUserRealname apUser = apUserRealnameMapper.selectOne
                        (Wrappers.<ApUserRealname>lambdaQuery()
                            .eq(ApUserRealname::getUserId, id));

        //2. 如果已经存在   抛异常 结束方法
        if(apUser != null){
            CustException.cust(AppHttpCodeEnum.DATA_EXIST,"实名认证信息已存在");
            return false;
        }
        //3. 如果不存在 返回true
        return true;
    }

    /**
     * 检查文件后缀 是否支持
     * @param originalFilename
     * @return
     */
    private boolean checkFileSuffix(String originalFilename) {
        if (StringUtils.isNotBlank(originalFilename)) {
            List<String> allowSuffix = Arrays.asList(".jpg", ".jpeg", ".png", ".gif");
            for (String suffix : allowSuffix) {
                if(originalFilename.endsWith(suffix)){
                    return true;
                }
            }
        }
        return false;
    }

    @Autowired
    WemediaFeign wemediaFeign;
    /**
     * 开通自媒体用户信息
     * @param apUser
     * @return
     */
    private WmUser createWmUser(ApUser apUser) {
        //1. 远程根据用户名 查询自媒体用户信息
        ResponseResult<WmUser> result = wemediaFeign.findByName(apUser.getName());
//        result.checkCode() == true  远程调用成功   false 远程调用失败
        if(!result.checkCode()){
            // 远程调用失败
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,result.getErrorMessage());
        }
        // 获取返回值
        WmUser wmUser = result.getData();
        //2. 如果已经存在   抛异常 结束方法
        if(wmUser!=null){
            CustException.cust(AppHttpCodeEnum.DATA_EXIST,"自媒体账户已存在");
        }
        //3. 如果不存在，基于apUser 创建 wmUser 并远程调用保存方法
        wmUser = new WmUser();
        wmUser.setApUserId(apUser.getId());
        wmUser.setName(apUser.getName());
        wmUser.setPassword(apUser.getPassword());
        wmUser.setSalt(apUser.getSalt());
        wmUser.setImage(apUser.getImage());
        wmUser.setPhone(apUser.getPhone());
        wmUser.setStatus(9);
        wmUser.setType(0);
        wmUser.setCreatedTime(new Date());
        ResponseResult<WmUser> saveResult = wemediaFeign.save(wmUser);
        if(!saveResult.checkCode()){
            // 远程调用失败
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,result.getErrorMessage());
        }
        return saveResult.getData();
    }

    @Autowired
    ArticleFeign articleFeign;

    /**
     * 远程创建作者信息
     * @param apUser
     * @param wmUser
     */
    private void createApAuthor(ApUser apUser, WmUser wmUser) {
        // 1. 根据用户id 查询作者信息是否存在
        ResponseResult<ApAuthor> result = articleFeign.findByUserId(apUser.getId());
        if(!result.checkCode()){
            // 远程调用失败
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,result.getErrorMessage());
        }
        ApAuthor apAuthor = result.getData();
        // 2. 如果作者存在 抛出异常 提示已存在
        if(apAuthor!=null){
            CustException.cust(AppHttpCodeEnum.DATA_EXIST,"作者信息已存在");
        }
        apAuthor = new ApAuthor();
        // 3. 如果作者不存在  创建作者信息  并 远程保存
        apAuthor.setName(apUser.getName());
        apAuthor.setType(2);
        apAuthor.setUserId(apUser.getId());
        apAuthor.setCreatedTime(new Date());
        apAuthor.setWmUserId(wmUser.getId());
        ResponseResult saveResult = articleFeign.save(apAuthor);
        if(!saveResult.checkCode()){
            // 远程调用失败
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR,result.getErrorMessage());
        }
    }
}
